/*
 +----------------------------------------------------------------------+
 |  APM stands for Alternative PHP Monitor                              |
 +----------------------------------------------------------------------+
 | Copyright (c) 2008-2009  Davide Mendolia, Patrick Allaert            |
 +----------------------------------------------------------------------+
 | This source file is subject to version 3.01 of the PHP license,      |
 | that is bundled with this package in the file LICENSE, and is        |
 | available through the world-wide-web at the following url:           |
 | http://www.php.net/license/3_01.txt                                  |
 | If you did not receive a copy of the PHP license and are unable to   |
 | obtain it through the world-wide-web, please send a note to          |
 | license@php.net so we can mail you a copy immediately.               |
 +----------------------------------------------------------------------+
 */

#include <sqlite3.h>
#include <zend.h>
#include <zend_API.h>
#include "php_apm.h"

static int perform_db_access_checks();


#define DB_FILE "/events"

sqlite3 *event_db;
char *db_file;

int apm_sqlite3_minit()
{
    if (perform_db_access_checks() == FAILURE) {
            return FAILURE;
    }
    /* Defining full path to db file */
    db_file = (char *) malloc((strlen(APM_G(db_path)) + strlen(DB_FILE) + 1) * sizeof(char));

    strcpy(db_file, APM_G(db_path));
    strcat(db_file, DB_FILE);
    return SUCCESS;
}

int apm_sqlite3_mshutdown()
{
    return SUCCESS;
}

int apm_sqlite3_rinit()
{
    /* Opening the sqlite database file */
    if (sqlite3_open(db_file, &event_db)) {
            /*
             Closing DB file and stop loading the extension
             in case of error while opening the database file
             */
            sqlite3_close(event_db);
            return FAILURE;
    }

    sqlite3_busy_timeout(event_db, APM_G(timeout));

    /* Making the connection asynchronous, not waiting for data being really written to the disk */
    sqlite3_exec(event_db, "PRAGMA synchronous = OFF", NULL, NULL, NULL);
    return SUCCESS;
}

int apm_sqlite3_rshutdown(float duration, char *script_filename)
{
    char *sql;

    /* Building SQL insert query */
    sql = sqlite3_mprintf("INSERT INTO slow_request (ts, duration, file) VALUES (datetime(), %f, %Q);",
                          duration, script_filename);

    /* Executing SQL insert query */
    sqlite3_exec(event_db, sql, NULL, NULL, NULL);

    sqlite3_free(sql);
    return SUCCESS;
}

/* Perform access checks on the DB path */
static int perform_db_access_checks()
{
	struct stat db_path_stat;

	/* Does db_path exists ? */
	if (stat(APM_G(db_path), &db_path_stat) != 0) {
		zend_error(E_CORE_WARNING, "APM cannot be loaded, an error occured while accessing %s", APM_G(db_path));
		return FAILURE;
	}

	/* Is this a directory ? */
	if (! S_ISDIR(db_path_stat.st_mode)) {
		zend_error(E_CORE_WARNING, "APM cannot be loaded, %s should be a directory", APM_G(db_path));
		return FAILURE;
	}

	/* Does it have the correct permissions ? */
	if (access(APM_G(db_path), R_OK | W_OK | X_OK) != 0) {
		zend_error(E_CORE_WARNING, "APM cannot be loaded, %s should be readable, writable and executable", APM_G(db_path));
		return FAILURE;
	}
	return SUCCESS;
}

/* Insert an event in the backend */
void apm_sqlite3_insert_event(int type, char *error_filename, uint error_lineno, char *msg, char *backtrace)
{
    /* sql variables */
    char *sql;

    /* Builing SQL insert query */
    sql = sqlite3_mprintf("INSERT INTO event (ts, type, file, line, message, backtrace) VALUES (datetime(), %d, %Q, %d, %Q, %Q);",
                              type, error_filename, error_lineno, msg, backtrace);
    /* Executing SQL insert query */
    sqlite3_exec(event_db, sql, NULL, NULL, NULL);

    sqlite3_free(sql);
}

static int odd_event_list = 1;
static int odd_slow_request = 1;

/* Function called for every row returned by event query */
static int callback(void *data, int num_fields, char **fields, char **col_name)
{
	php_printf("<tr class=\"%s %s\"><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td><pre>%s</pre></td></tr>\n",
                   fields[2], odd_event_list ? "odd" : "even", fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6]);
	odd_event_list = !odd_event_list;

	return 0;
}

/* Function called for every row returned by slow request query */
static int callback_slow_request(void *data, int num_fields, char **fields, char **col_name)
{
	php_printf("<tr class=\"%s\"><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n",
                   odd_slow_request ? "odd" : "even", fields[0], fields[1], fields[2], fields[3]);
	odd_slow_request = !odd_slow_request;

	return 0;
}

int apm_sqlite3_get_events()
{
    sqlite3 *db;
    /* Opening the sqlite database file */
    if (sqlite3_open(db_file, &db)) {
            /* Closing DB file and returning false */
            sqlite3_close(db);
            return 0;
    }

    /* Results are printed in an HTML table */
    odd_event_list = 1;
    php_printf("<table id=\"event-list\"><tr><th>#</th><th>Time</th><th>Type</th><th>File</th><th>Line</th><th>Message</th><th>Backtrace</th></tr>\n");
    sqlite3_exec(db, "SELECT id, ts, CASE type \
                      WHEN 1 THEN 'E_ERROR' \
                      WHEN 2 THEN 'E_WARNING' \
                      WHEN 4 THEN 'E_PARSE' \
                      WHEN 8 THEN 'E_NOTICE' \
                      WHEN 16 THEN 'E_CORE_ERROR' \
                      WHEN 32 THEN 'E_CORE_WARNING' \
                      WHEN 64 THEN 'E_COMPILE_ERROR' \
                      WHEN 128 THEN 'E_COMPILE_WARNING' \
                      WHEN 256 THEN 'E_USER_ERROR' \
                      WHEN 512 THEN 'E_USER_WARNING' \
                      WHEN 1024 THEN 'E_USER_NOTICE' \
                      WHEN 2048 THEN 'E_STRICT' \
                      WHEN 4096 THEN 'E_RECOVERABLE_ERROR' \
                      WHEN 8192 THEN 'E_DEPRECATED' \
                      WHEN 16384 THEN 'E_USER_DEPRECATED' \
                      END, \
                      file, line, message, backtrace FROM event ORDER BY id DESC", callback, NULL, NULL);
    php_printf("</table>");

    sqlite3_close(db);
    return 1;
}

int apm_get_slow_requests()
{
    sqlite3 *db;
    /* Opening the sqlite database file */
    if (sqlite3_open(db_file, &db)) {
            /* Closing DB file and returning false */
            sqlite3_close(db);
            return 0;
    }

    /* Results are printed in an HTML table */
    odd_slow_request = 1;
    php_printf("<table id=\"slow-request-list\"><tr><th>#</th><th>Time</th><th>Duration</th><th>File</th></tr>\n");
    sqlite3_exec(db, "SELECT id, ts, duration, file FROM slow_request ORDER BY id DESC", callback_slow_request, NULL, NULL);
    php_printf("</table>");

    sqlite3_close(db);
    return 1;
}